package top.waikin.mooc.portal.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import top.waikin.mooc.common.api.ResultCode;
import top.waikin.mooc.common.exception.Asserts;
import top.waikin.mooc.entity.Course;
import top.waikin.mooc.entity.User;
import top.waikin.mooc.mapper.CourseMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import top.waikin.mooc.portal.domain.CourseDto;
import top.waikin.mooc.portal.domain.CourseQuery;
import top.waikin.mooc.portal.service.CourseChannelService;
import top.waikin.mooc.portal.service.CourseService;
import top.waikin.mooc.portal.service.UserService;

/**
 * 课程 服务实现类
 */
@Service
public class CourseServiceImpl extends ServiceImpl<CourseMapper, Course> implements CourseService {

    @Autowired
    UserService userService;

    @Autowired
    CourseChannelService courseChannelService;

    @Override
    public int create(CourseDto courseDto) {
        Course course = new Course();
        BeanUtil.copyProperties(courseDto, course);
        course.setUserId(userService.getCurrentUser().getId());
        course.setEnrollCount(0);
        course.setMarkCount(0);
        course.setAverageMark(0f);
        save(course);
        courseChannelService.increaseCourseCount(course.getChannelId());
        return course.getId();
    }

    @Override
    public boolean delete(Integer id) {
        checkPermission(id);
        Course course = getById(id);
        removeById(id);
        courseChannelService.decreaseCourseCount(course.getChannelId());
        return true;
    }

    @Override
    public boolean update(Integer id, CourseDto courseDto) {
        checkPermission(id);
        Course oldCourse = getById(id);
        Course newCourse = new Course();
        BeanUtil.copyProperties(courseDto, newCourse);
        newCourse.setId(id);
        updateById(newCourse);
        // 先减少再增加
        courseChannelService.decreaseCourseCount(oldCourse.getChannelId());
        courseChannelService.increaseCourseCount(newCourse.getChannelId());
        return true;
    }

    @Override
    public IPage<Course> pageTeach(Integer current, Integer size, String name) {
        User currentUser = userService.getCurrentUser();
        Wrapper<Course> wrapper = new LambdaQueryWrapper<Course>().eq(Course::getUserId, currentUser.getId())
                .like(StrUtil.isNotBlank(name), Course::getName, name);
        return page(new Page<>(current, size), wrapper);
    }

    @Override
    public IPage<Course> pagePublic(Integer current, Integer size, String name, CourseQuery courseQuery) {
        Wrapper<Course> wrapper = new LambdaQueryWrapper<Course>().eq(Course::getIsPublic, 1)
                .like(StrUtil.isNotBlank(name), Course::getName, name)
                .eq(ObjectUtil.isNotNull(courseQuery.getChannelId()), Course::getChannelId, courseQuery.getChannelId())
                .orderByDesc(ObjectUtil.isNotNull(courseQuery.getEnrollCountDesc()), Course::getEnrollCount)
                .orderByDesc(ObjectUtil.isNotNull(courseQuery.getAverageMarkDesc()), Course::getAverageMark);
        return page(new Page<>(current, size), wrapper);
    }

    public void checkPermission(Integer id) {
        Course course = getById(id);
        User currentUser = userService.getCurrentUser();
        if (ObjectUtil.isNull(course) || ObjectUtil.notEqual(course.getUserId(), currentUser.getId())) {
            Asserts.fail(ResultCode.FORBIDDEN);
        }
    }

    @Override
    public void increaseEnrollCount(Integer courseId) {
        Wrapper<Course> wrapper = new LambdaUpdateWrapper<Course>().eq(Course::getId, courseId)
                .setSql("enroll_count = enroll_count + 1");
        update(wrapper);
    }

    @Override
    public void decreaseEnrollCount(Integer courseId) {
        Wrapper<Course> wrapper = new LambdaUpdateWrapper<Course>().eq(Course::getId, courseId)
                .setSql("enroll_count = enroll_count - 1");
        update(wrapper);
    }

    @Override
    public void addMark(Integer courseId, Integer mark) {
        Wrapper<Course> wrapper = new LambdaUpdateWrapper<Course>().eq(Course::getId, courseId)
                .setSql("average_mark = ( average_mark * mark_count + " + mark + " ) / ( mark_count + 1) ," +
                        "mark_count = mark_count + 1");
        update(wrapper);
    }

    @Override
    public void deleteMark(Integer courseId, Integer mark) {
        Wrapper<Course> wrapper = new LambdaUpdateWrapper<Course>().eq(Course::getId, courseId)
                .setSql("average_mark = ( average_mark * mark_count - " + mark + " ) / ( mark_count - 1 ) ," +
                        "mark_count = mark_count - 1");
        update(wrapper);
    }

    @Override
    public void updateMark(Integer courseId, Integer oldMark, Integer newMark) {
        Wrapper<Course> wrapper = new LambdaUpdateWrapper<Course>().eq(Course::getId, courseId)
                .setSql("average_mark = ( average_mark * mark_count +( " + newMark + " - " + oldMark + " ))/ mark_count ");
        update(wrapper);
    }
}
